package export;

import common.statistics.Series;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jxl.read.biff.BiffException;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
import neuron.Dendrite;
import neuron.Network;
import neuron.NetworkStatistics;
import neuron.Neuron;
import neuron.NeuronFilter;
import neuron.NeuronStatistics;
import neuron.Segment;
import neuron.file.AuxData;
import neuron.file.GenericNeuronLoader;
import neuron.file.NeuroLucidaLoader;

public class StatisticsToExcelExport {
 
	ExcelWriter out = null;
 
	
	
	StatisticsCollection globalBasal = new StatisticsCollection();
	StatisticsCollection globalApical = new StatisticsCollection();
	StatisticsCollection globalOblique = new StatisticsCollection();
	StatisticsCollection globalTuft = new StatisticsCollection();
	
	private void makeStatRow(Neuron n) throws RowsExceededException, WriteException {

		String fileName = (String) n.getMetadata("FILE");
		if (fileName == null || fileName.length() == 0) fileName = "<Neuron>";
		out.write(fileName);
		

		// BASAL STATISTICS
		StatisticsCollection basalStats = new StatisticsCollection();
		for (Dendrite d : n.basalDendrites()) {
			basalStats.add(d);
			globalBasal.add(d);
		}
		basalStats.write(out);
		
		// APICAL STATS
		StatisticsCollection apicalStats = new StatisticsCollection();
		apicalStats.add(n.getApicalDendrite());
		globalApical.add(n.getApicalDendrite());
		apicalStats.write(out);

		// max-y, soma-pia
		out.write(n.getApicalDendrite().maxY());
		if (n.getNetwork() == null || n.getNetwork().getLayers() == null) {
			out.write("");
		} else {
			out.write(n.getNetwork().getLayers().pia);
		}
		
		// OBLIQUE STATISTICS
		StatisticsCollection obliqueStats = new StatisticsCollection();
		for (Dendrite d : n.getApicalDendrite().getObliqueDendrites()) {
			obliqueStats.add(d);
			globalOblique.add(d);
		}
		obliqueStats.write(out);

		// TUFT STATISTICS
		StatisticsCollection tuftStats = new StatisticsCollection();
		Dendrite d = n.getApicalDendrite().getTuftDendrite();
		tuftStats.add(d);
		globalTuft.add(d);
		tuftStats.write(out);
		
		// short segs - basal, oblique, tuft
		int l10=0, l20=0, l30=0, l50=0;
		for (Dendrite dd : n.basalDendrites()) {
			List<Segment> lst = dd.getTerminalSegments();
			for (Segment s : lst) {
				if (s.length() < 10) { l10++; }
				else if (s.length() < 20) { l20++; }
				else if (s.length() < 30) { l30++; }
				else if (s.length() < 50) { l50++; }
			}
		}
		out.write(l10);
		out.write(l20);
		out.write(l30);
		out.write(l50);
		l10=0; l20=0; l30=0; l50=0;
		for (Dendrite dd : n.getApicalDendrite().getObliqueDendrites()) {
			List<Segment> lst = dd.getTerminalSegments();
			for (Segment s : lst) {
				if (s.length() < 10) { l10++; }
				else if (s.length() < 20) { l20++; }
				else if (s.length() < 30) { l30++; }
				else if (s.length() < 50) { l50++; }
			}
		}
		out.write(l10);
		out.write(l20);
		out.write(l30);
		out.write(l50);
		d = n.getApicalDendrite().getTuftDendrite();
		List<Segment> lst = d.getTerminalSegments();
		l10=0; l20=0; l30=0; l50=0;
		for (Segment s : lst) {
			if (s.length() < 10) { l10++; }
			else if (s.length() < 20) { l20++; }
			else if (s.length() < 30) { l30++; }
			else if (s.length() < 50) { l50++; }
		}
		out.write(l10);
		out.write(l20);
		out.write(l30);
		out.write(l50);
		// --- short segs ---

		// -- angle/turning data --
		NeuronStatistics ns = n.statistics();
		out.write(ns.basalBranchAngles().mean());
		out.write(ns.basalBranchAngles().stdev());

		out.write(ns.obliqueBranchAngles().mean());
		out.write(ns.obliqueBranchAngles().stdev());

		out.write(ns.tuftBranchAngles().mean());
		out.write(ns.tuftBranchAngles().stdev());

		// turning
		out.write(ns.basalTurningAngles().mean());
		out.write(ns.basalTurningAngles().stdev());

		out.write(ns.obliqueTurningAngles().mean());
		out.write(ns.obliqueTurningAngles().stdev());

		out.write(ns.tuftTurningAngles().mean());
		out.write(ns.tuftTurningAngles().stdev());

		// sholl
		for (int i : ns.basalSholl(10)) out.write(i);

		out.newLine();
	} 
	
	private void statExport(File datadir, File excel) throws BiffException, IOException, ParseException, WriteException
	{
		out = new ExcelWriter(excel);
		out.goToSheet("Statistics");
		
        // write headers
        String[] hdrs = new String[] { "File", 
        		"BASAL N#","# TS", "sd", "Asym", "sd", "TotLen", "mean", "sd", "ISLen", "sd", "TSLen", "sd",
        		"APICAL N#","# TS", "sd", "Asym", "sd", "TotLen", "mean", "sd", "ISLen", "sd", "TSLen", "sd",
        		"MaxY", "PiaToSoma",
        		"OBLIQUE N#","# TS", "sd", "Asym", "sd", "TotLen", "mean", "sd", "ISLen", "sd", "TSLen", "sd",
        		"TUFT N#","# TS", "sd", "Asym", "sd", "TotLen", "mean", "sd", "ISLen", "sd", "TSLen", "sd",
        		"ShortBasal<10um", "Basal<20um", "Basal<30um", "Basal<50um", "Oblique<10um", "Oblique<20um", "Oblique<30um", "Oblique<50um", "Tuft<10um", "Tuft<20um", "Tuft<30um", "Tuft<50um",
				"Branching Angle Basal", "sd", "Oblique", "sd", "Tuft", "sd",
				"Turning Angle Basal", "sd", "Oblique", "sd", "Tuft", "sd",
				"Sholl (basal, step=10um)"
        		};
        out.write(hdrs);
		out.newLine();

        // Write data
		File[] files = datadir.listFiles(FileFilters.neuroFileFilter);
		List<File> sorted = new ArrayList<File>();
		List<Network> nets = new ArrayList<Network>();
		for (File f : files) sorted.add(f);
		Collections.sort(sorted);
		for (File f : sorted) {
			Network net = GenericNeuronLoader.load(f);
			AuxData.setNetworkLayers(net);
			for (Neuron n : net) {
//			Neuron n = net.get(0);
				NeuronFilter.filterNeuron(n);
				makeStatRow(n);
			}
			nets.add(net);
		}

		System.out.println("## GROUP ##");
		
		// global stats
		out.newLine();
		out.write("## GROUP ##");
		globalBasal.write(out);
		globalApical.write(out);
		out.write("--"); out.write("--");
		globalOblique.write(out);
		globalTuft.write(out);

		// short segments
		for (int i = 0; i < 12; i++) out.write("--");

		// spatial
		NetworkStatistics ns = new NetworkStatistics(nets);

		out.write(ns.basalBranchingAngles().mean());
		out.write(ns.basalBranchingAngles().stdev());
		out.write(ns.obliqueBranchingAngles().mean());
		out.write(ns.obliqueBranchingAngles().stdev());
		out.write(ns.tuftBranchingAngles().mean());
		out.write(ns.tuftBranchingAngles().stdev());

		out.write(ns.basalTurningAngles().mean());
		out.write(ns.basalTurningAngles().stdev());
		out.write(ns.obliqueTurningAngles().mean());
		out.write(ns.obliqueTurningAngles().stdev());
		out.write(ns.tuftTurningAngles().mean());
		out.write(ns.tuftTurningAngles().stdev());

		Series[] sholl = ns.basalSholl();
		for (Series s : sholl) out.write(s.mean());

		// write file
		out.close();
	}
	
	
	/**
	 * Export statistics of all files in directory <dir>
	 * Asks user to choose a file where the statistics are written.
	 * 
	 * @param dir
	 */
	public void exportFilesInDir(File dir, File dest) throws IOException
	{
		if (dir == null || dest == null || !dir.isDirectory()) throw new IOException("internal error: Bad arguments to exportFilesInDir!");
		
		try {
			statExport(dir, dest);
		} catch (Exception e) {
			throw new IOException(e);
		}
	}
}
